<?php

namespace App\Service\CallingCards\Article;

use App\Enums\BaseErrorCode;
use App\Exceptions\Custom\ResponseHttpException;
use App\Models\FeishuCallingCard\FeishuCallingCardsArticle;
use App\Service\Common\OssService;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Str;
use Kuafu\File\FileUtil;

class WechatArticleService
{
    protected string $tmpDir = '';

    const ARTICLE_SAVE_CACHE = 'wechatArticleSave:';

    /**
     * 微信服务号文章信息
     * @param int $id
     * @return array
     */
    public function getArticleInfo(int $id): array
    {
        $info = FeishuCallingCardsArticle::query()
            ->where('id', $id)
            ->where('type', FeishuCallingCardsArticle::ARTICLE_TYPE_40)
            ->first();
        if (!$info) {
            throw new ResponseHttpException(BaseErrorCode::$ERROR_OCCURRED, null, '未找到文章');
        }
        $info  = $info->toArray();
        $extra = json_decode($info['extra'], true);
        return [
            'id'          => $id,
            'title'       => $info['name'],
            'status'      => $info['status'],
            'description' => $info['content'],
            'imageUrl'    => FileUtil::domain() . '/' . ltrim($info['thumbImage'], '/'),
            'htmlUrl'     => $extra['htmlUrl'] ?? '',
            'wechatUrl'   => $extra['wechatUrl'] ?? '',
            'htmlContent' => file_get_contents($extra['htmlUrl'] ?? ''),
        ];
    }

    /**
     * 微信公众号文章内容保存
     * @param string $wechatUrl
     * @param int $userId
     * @return array
     */
    public function wechatArticleSave(string $wechatUrl, int $userId): array
    {
        $key = self::ARTICLE_SAVE_CACHE;
        if (Redis::get($key)) {
            throw new ResponseHttpException(BaseErrorCode::$ERROR_OCCURRED, null, '已存在文章下载任务，请稍等');
        }
        $setnxRes = Redis::setnx($key, 1);
        if ($setnxRes) {
            Redis::expire($key, 20);
        }
        $info = self::getWechatArticleDownload($wechatUrl);
        if (!$info['htmlUrl']) {
            throw new ResponseHttpException(BaseErrorCode::$ERROR_OCCURRED, null, '内容保存失败');
        }
        $article = new FeishuCallingCardsArticle;

        $extra = [
            'wechatUrl' => $info['wechatUrl'],
            'htmlUrl'   => $info['htmlUrl'],
            'path'      => $info['path'],
        ];

        $article->name          = $info['title'];
        $article->content       = $info['description'];
        $article->thumbImage    = ltrim($info['image'], '/');
        $article->originalImage = ltrim($info['image'], '/');
        $article->extra         = json_encode($extra);
        $article->status        = FeishuCallingCardsArticle::STATUS_ON;
        $article->type          = FeishuCallingCardsArticle::ARTICLE_TYPE_40;
        $article->createdByUid  = $userId;
        $article->save();

        Redis::del($key);
        return [];
    }

    /**
     * 微信公众号文章内容下载
     * @param string $url
     * @return array
     */
    public function getWechatArticleDownload(string $url): array
    {
        $title       = '';
        $description = '';
        $image       = '';
        try {
            $html = file_get_contents($url);

            $html = str_get_html($html);

            self::prepareTmpDir();

            // 获取文章标题，描述，分享图片
            collect($html->find('meta'))->map(
                function ($item) use (&$title, &$image, &$description) {
                    switch ($item->property) {
                        case 'og:title':
                            $title = $item->content;
                            break;
                        case 'og:image':
                            $imageContent = $item->content;
                            $path_parts   = pathinfo($imageContent);
                            $suffix       = $path_parts['extension'] ?? '';
                            if (!$suffix) {
                                $suffix = 'png';
                            }
                            $md5       = md5($imageContent);
                            $localPath = $this->tmpDir . '/' . $md5 . '.' . $suffix;

                            self::streamDownload($localPath, $imageContent);

                            // 上传到oss
                            $res   = OssService::uploadFile($localPath, $suffix);
                            $image = $res['path'] ?? '';
                            break;
                        case 'og:description':
                            $description = $item->content;
                            break;
                    }
                }
            );

            // 获取文章中js,css,图片 进行下载并替换
            $linkHref   = collect($html->find('link'))->pluck('href')->toArray();
            $scriptSrc  = collect($html->find('script'))->pluck('src')->toArray();
            $imgDataSrc = collect($html->find('img'))->pluck('data-src')->toArray();
            $imgSrc     = collect($html->find('img'))->pluck('src')->toArray();

            $total = array_merge($linkHref, $scriptSrc, $imgDataSrc, $imgSrc);

            $count = 0;

            foreach ($total as $v) {
                if (!$v) {
                    continue;
                }

                $old = $v;

                // 下载
                if (strpos($v, 'https:') !== 0 && strpos($v, 'http:') !== 0) {
                    $v = 'https:' . $v;
                }
                $path_parts = pathinfo($v);

                $suffix = $path_parts['extension'] ?? '';
                if (in_array($suffix, ['com', 'cn'])) {
                    continue;
                }
                if (!$suffix) {
                    $suffix = 'png';
                }
                if ($suffix == 'js') {
                    $res['url'] = '';
                } else {
                    $md5       = md5($v);
                    $localPath = $this->tmpDir . '/' . $md5 . '.' . $suffix;

                    self::streamDownload($localPath, $v);

                    // 上传到oss
                    $res = OssService::uploadFile($localPath, $suffix);

                }

                // 替换
                $count++;
                $html = str_replace($old, $res['url'], $html);

            }

            $html = str_replace(
                '</body>', "<script>
    document.addEventListener('DOMContentLoaded', function() {
        const images = document.querySelectorAll('img[data-src]');
        images.forEach(image => {
            image.setAttribute('src', image.getAttribute('data-src'));
            image.removeAttribute('data-src');
        });
    });
  </script>
  </body>", $html
            );

            $htmlMd5       = md5($url);
            $suffix        = 'html';
            $htmlLocalPath = $this->tmpDir . '/' . $htmlMd5 . '.' . $suffix;

            file_put_contents($htmlLocalPath, $html, FILE_APPEND);

            $res = OssService::uploadFile($htmlLocalPath, $suffix);

            if (is_dir($this->tmpDir)) {
                app(Filesystem::class)->deleteDirectory($this->tmpDir, true);
                @rmdir($this->tmpDir);
            }
        } catch (\Exception $e) {
            Log::info(
                'getWechatArticleDownload',
                [
                    'wechatUrl' => $url,
                    'msg'       => $e->getMessage(),
                ]
            );
        }

        return [
            'title'       => $title,
            'description' => $description,
            'image'       => $image,
            'wechatUrl'   => $url,
            'htmlUrl'     => $res['url'] ?? '',
            'path'        => $res['path'] ?? '',
        ];
    }

    protected function prepareTmpDir()
    {
        $this->tmpDir = sys_get_temp_dir() . '/weixin/file_' . Str::random(8);
        //        $this->tmpDir = app()->basePath() . '/public/storage/weixin/file_' . Str::random(8);
        if (is_dir($this->tmpDir)) {
            // 清空这个dir
            app(Filesystem::class)->deleteDirectory($this->tmpDir, true);
            @rmdir($this->tmpDir);
        }
        mkdir($this->tmpDir, 0777, true);
    }

    protected static function streamDownload(string $localPath, string $url)
    {
        $fd = $local = null;
        @unlink($localPath);
        try {
            $basepath = base_path($localPath);
            if (!is_dir($basepath)) {
                mkdir($basepath, 0777, true);
            }
            $local = fopen($localPath, 'wb');
            $fd    = fopen($url, 'rb');
            while (!feof($fd)) {
                fwrite($local, fread($fd, 1024 * 1024), 1024 * 1024);
            }
        } catch (\Exception $e) {
            var_dump($e->getMessage());
        }
        is_resource($fd) && fclose($fd);
        is_resource($local) && fclose($local);
    }
}
